package com.idanchuang.count.service.impl;

import com.idanchuang.count.core.entity.IncrementCommand;
import com.idanchuang.count.core.entity.ResetCommand;
import com.idanchuang.count.core.model.BizProperties;
import com.idanchuang.count.dao.CountDao;
import com.idanchuang.count.service.UserCountService;
import com.idanchuang.user.count.service.api.request.command.count.SceneCountIncrement;
import com.idanchuang.user.count.service.api.request.command.count.UserMultiScenesCountResetCommand;
import com.idanchuang.user.count.service.api.request.command.count.UserMultiScenesCountUpdateCommand;
import com.idanchuang.user.count.service.api.request.command.count.UserSceneCountUpdateCommand;
import com.idanchuang.user.count.service.api.request.query.count.UserCountBatchQuery;
import com.idanchuang.user.count.service.api.request.query.count.UserCountQuery;
import com.idanchuang.user.count.service.api.response.UserMultiScenesCountDTO;
import com.idanchuang.user.count.service.api.response.UserSceneCountDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.idanchuang.count.core.util.CountDtoAssembler.buildUserMultiScenesCountDTO;
import static com.idanchuang.count.core.util.CountDtoAssembler.buildUserSceneCountDTO;
import static com.idanchuang.count.core.util.keyformat.UserCountKeysFormatter.formatUserSceneCountKey;
import static com.idanchuang.count.core.util.keyformat.UserCountKeysFormatter.formatUserSceneCountKeyList;

/**
 * @author Richard_yyf
 * @version 1.0 2021/5/27
 */
@Service
@Slf4j
public class UserCountServiceImpl implements UserCountService {

    @Resource
    private CountDao countDao;

    @Resource
    private BizProperties bizProperties;

    @Override
    public void count(List<UserSceneCountUpdateCommand> commands) {

        List<IncrementCommand> incrementCommands = new ArrayList<>(commands.size());

        commands.forEach(command -> incrementCommands.add(convertFrom(command)));

        countDao.batchIncr(incrementCommands);
    }

    @Override
    public void multiCount(List<UserMultiScenesCountUpdateCommand> commands) {

        // UserMultiScenesCountUpdateCommand 至少有 2个计数变化，所以size 设置成两倍，防止arrayList频繁扩容
        List<IncrementCommand> incrementCommands = new ArrayList<>(commands.size() * 2);

        commands.forEach(command -> incrementCommands.addAll(convertListFrom(command)));

        countDao.batchIncr(incrementCommands);
    }

    @Override
    public void batchResetCount(List<UserMultiScenesCountResetCommand> countResetCommand) {

        List<ResetCommand> resetCommands = new ArrayList<>(countResetCommand.size() * 2);

        countResetCommand.forEach(command -> resetCommands.addAll(convertListFrom(command)));

        countDao.batchSet(resetCommands);
    }

    @Override
    public UserSceneCountDTO query(UserCountQuery query) {

        Integer sceneCount = Math.toIntExact(countDao.getCount(formatUserSceneCountKey(bizProperties.getBizId(query.getBizCode()), query.getUserCode(), query.getScene())));

        return buildUserSceneCountDTO(query.getUserCode(), query.getScene(), sceneCount);
    }

    @Override
    public UserMultiScenesCountDTO batchQuery(UserCountBatchQuery query) {

        List<String> keys = formatUserSceneCountKeyList(bizProperties.getBizId(query.getBizCode()), query.getUserCode(), query.getSceneList());

        // 直接返回 list of count，然后根据顺序来拼装 scenesCountMap
        List<Long> countValues = countDao.multiGetCount(keys);

        Map<String, Integer> scenesCountMap = new HashMap<>(query.getSceneList().size());
        for (int i = 0; i < query.getSceneList().size(); i++) {
            if (countValues.get(i) == null) {
                continue;
            }
            scenesCountMap.put(query.getSceneList().get(i), Math.toIntExact(countValues.get(i)));
        }

        return buildUserMultiScenesCountDTO(query.getUserCode(), scenesCountMap);
    }

    private List<ResetCommand> convertListFrom(UserMultiScenesCountResetCommand command) {
        Integer userCode = command.getUserCode();
        Integer bizId = bizProperties.getBizId(command.getBizCode());

        return command.getSceneCountResetList().stream()
                .map(c -> new ResetCommand(
                        formatUserSceneCountKey(bizId, userCode, c.getScene()), c.getCount()))
                .collect(Collectors.toList());
    }

    private List<IncrementCommand> convertListFrom(UserMultiScenesCountUpdateCommand command) {

        Integer userCode = command.getUserCode();
        Integer bizId = bizProperties.getBizId(command.getBizCode());

        return command.getSceneCountIncrements().stream()
                .map(c -> new IncrementCommand(
                        formatUserSceneCountKey(bizId, userCode, c.getScene()), c.getIncrementAmount()))
                .collect(Collectors.toList());
    }

    private IncrementCommand convertFrom(UserSceneCountUpdateCommand command) {

        SceneCountIncrement sceneCountIncrement = command.getSceneCountIncrement();

        String key = formatUserSceneCountKey(bizProperties.getBizId(command.getBizCode()), command.getUserCode(), sceneCountIncrement.getScene());

        return new IncrementCommand(key, sceneCountIncrement.getIncrementAmount());
    }
}
